package com.lvhx.java8;

import com.google.common.collect.Maps;
import com.lvhx.base.Employee;
import com.lvhx.base.EmployeeData;
import lombok.Data;
import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * 1、Stream：流    数据源转化成流------>中间操作------>新的流（源数据不会改变）
 * 2、多个中间操作形成一个流水线，除非触发终止操作，否则不会进行任何处理，在终止操作时一次性处理完，这种情况叫做【惰性求值】
 */
public class StreamAPITest {
    private List<Employee> employees = EmployeeData.getEmployees();

    /**
     * 创建方式
     */
    @Test
    public void test1(){
        //1、通过集合
        List<Employee> employees = EmployeeData.getEmployees();
        //顺序流
        Stream<Employee> stream = employees.stream();
        //并行流
        Stream<Employee> employeeStream = employees.parallelStream();

        //2、通过数组
        int[] arr = new int[]{1,2,3};
        //IntStream LongStream DoubleStream
        IntStream stream1 = Arrays.stream(arr);
//        Stream<Integer> stream2 = (Stream<Integer>) Arrays.stream(arr);

        //3、通过Stream.of
        Stream<Integer> stream3 = Stream.of(1, 2, 3);

        //4、创建无限流
        Stream<Integer> limit = Stream.iterate(0, t -> t + 2).limit(10);    //迭代

        Stream<Double> limit1 = Stream.generate(Math::random).limit(10);    //生成
    }

    /**
     * 筛选与切片
     */
    @Test
    public void test2(){
        List<Employee> employees = EmployeeData.getEmployees();

        //1.filter(Predicate p) 	接收参数，从流中过滤某些元素
        Stream<Employee> stream1 = employees.stream().filter(e -> e.getAge() > 20);

        //2.distinct() 筛选，通过流所生成元素的 hashCode()和equals()去除重复元素
        Stream<Employee> stream = employees.stream().distinct();

        /**
         * 注意：在没有第三方注解的前提下，如果添加两个参数完全一样的bean，是不能实现去重效果的，比较的是内存地址
         * 通过生成元素的HashCode和equals方法进行去重
         * @TODO 不知道为什么，在bean上添加@Data注解，两个相同参数的bean的内存地址使用的是同一个（应该是@Data标签重写了bean的hashCode和equals方法）
         * 基本数据类型可以用这个方法进行去重，实体bean如果想去重，需要重写bean的hashCode和equals方法
         */
        Employee e1 = new Employee("A", 1, 2.0);
        Employee e2 = new Employee("A", 1, 2.0);
        System.out.println(e1.equals(e2));
//        List<Employee> list = Arrays.asList(new Employee("A", 1, 2.0), new Employee("A", 1, 2.0));
        List<Employee> list = Arrays.asList(e1, e2);
        List<Employee> collect = list.stream().distinct().collect(Collectors.toList());
        System.out.println(collect);


        //3.limit
        Stream<Employee> limit = employees.stream().limit(10);

        //4.skip(long n) 跳过元素，返回一个扔掉了前 n 个元素的流。
        //若流中元素不足 n 个，则返回一个空流。与 limit(n) 互补
        Stream<Employee> skip = employees.stream().skip(3);


    }

    /**
     * 映射
     */
    @Test
    public void test3(){
        List<Employee> employees = EmployeeData.getEmployees();

        //1.map(Function f)
        Stream<String> stream = employees.stream().map(e -> e.getName());
        //2.mapToDouble(ToDoubleFunction f) mapToInt(ToIntFunction f) mapToLong(ToLongFunction f)
        DoubleStream doubleStream = employees.stream().map(e -> e.getAge()).mapToDouble(e -> Double.parseDouble(String.valueOf(e)));

        //为什么要有flatMap,如果使用map，返回的是一个Stream<Stream<Character>>，操作起来不方便
        List<String> list1 = Arrays.asList("aaa", "bbb", "ccc");
        Stream<Stream<Character>> streamStream = list1.stream().map(StreamAPITest::filterCharacter);
        streamStream.forEach(s -> {
            s.forEach(System.out::print);
        });


        //3.flatMap(Function f) 接收一个函数作为参数，将流中的每个值都换成另一个流，然后把所有流连接成一个流
        employees.stream().flatMap(e -> {
            ArrayList<Integer> list = new ArrayList<>();
            for (int i = 0; i < e.getAge(); i++) {
                list.add(i);
            }
            return list.stream();
        });
    }


    /**
     * 排序
     * 自然排序----Comparable,一般类都实现了Comparable接口，里面有个compareto方法，按照这个方法排
     * 定制排序----Comparator
     */
    @Test
    public void test4(){
        List<Employee> employees = EmployeeData.getEmployees();

        //1、sorted()。产生一个新流，其中按自然顺序排序
        List<Integer> list = Arrays.asList(1,3,2,4,5,9,5,7,4);
        list.stream().sorted().forEach(System.out::println);

        //2、按比较器排序
        employees.stream().sorted((e1,e2) -> {
            if (e1.getAge() > e2.getAge()){
                return 1;
            }else {
                return -1;
            }
        }).forEach(System.out::println);


        //3.获取属性排序
        employees.stream().map(Employee::getAge).sorted(Comparator.reverseOrder()).collect(Collectors.toList()).forEach(System.out::println);

        //4.按bean中某一属性排序
        employees.stream().sorted(Comparator.comparing(Employee::getAge)).collect(Collectors.toList()).forEach(System.out::println);

        //5.按bean中某一属性【逆】排序
        employees.stream().sorted(Comparator.comparing(Employee::getAge).reversed()).collect(Collectors.toList()).forEach(System.out::println);

        System.out.println();
        //6.按bean中多个属性不同方向排序
        employees.stream().sorted(Comparator.comparing(Employee::getAge).reversed().thenComparing(Employee::getSalary)).collect(Collectors.toList()).forEach(System.out::println);
    }

    /**
     * 终止操作
     */
    @Test
    public void test5(){
        List<Employee> employees = EmployeeData.getEmployees();

        //1.检查是否匹配所有元素
        // anyMatch(Predicate p)	检查是否至少匹配一个元素
        //noneMatch(Predicate p)	检查是否没有匹配所有元素    是否没有匹配的元素，没有匹配的返回true，有匹配的返回false
        boolean b = employees.stream().filter(e -> e.getAge() > 20).allMatch(e -> e.getAge() > 20);

        //2.findFirst()返回第一个元素
        //findAny()	返回当前流中的任意元素
        //count()	返回流中元素总数
        Optional<Employee> first = employees.stream().filter(e -> e.getAge() > 20).findFirst();

        //3.max(Comparator c)	返回流中最大值  min(Comparator c)	返回流中最小值，需要指定方法
        Optional<Integer> max = employees.stream().flatMap(e -> Arrays.asList(e.getAge()).stream()).max(Integer::compare);

        //使用mapTo...方法，不需要指定API
        int asInt = employees.stream().mapToInt(Employee::getAge).max().getAsInt();

        //4.forEach(Consumer c)	内部迭代(使用 Collection 接口需要用户去做迭代，称为外部迭代。
        //相反，Stream API 使用内部迭代——它帮你把迭代做了)
        employees.stream().forEach(System.out::println);


        //5.reduce(T iden, BinaryOperator b)	可以将流中元素反复结合起来，得到一个值。返回 T
        Integer reduce = employees.stream().filter(e -> e.getAge() > 34).map(Employee::getAge).reduce(0, Integer::sum);
        System.out.println(reduce);
        //没有初始值，所以返回的是Optional
        Optional<Integer> reduce1 = employees.stream().map(Employee::getAge).reduce(Integer::sum);
        if (reduce1.isPresent())
            System.out.println(reduce1.get());

        //6.collect收集

    }

    /**
     * Collectors工具类
     */
    @Test
    public void test6(){
        List<Employee> employees = EmployeeData.getEmployees();

        LinkedList<Integer> collect = employees.stream().map(Employee::getAge).collect(Collectors.toCollection(LinkedList::new));

        Long collect1 = employees.stream().filter(e -> e.getAge() > 20).collect(Collectors.counting());

        //最小值，
        Integer integer = employees.stream().map(Employee::getAge).collect(Collectors.minBy(Comparator.naturalOrder())).get();

        //分区列表:partitioningBy()：返回Map<Boolean, List<String>>
        Map<Boolean, List<Employee>> collect2 = employees.stream().collect(Collectors.partitioningBy(e -> e.getAge() > 20));

        //连接元素：Joining()
        List<String> list = Arrays.asList("A","B","C");
        String collect4 = list.stream().collect(Collectors.joining("-","bejin","end"));


        //Long类型集合的平均值：averagingLong()
        List<Long> longValues = Arrays.asList(100L, 200L, 300L);
        Double d1 = longValues.stream().collect(Collectors.averagingLong(x -> x));
        System.out.println(d1);

        //整数求和
        //先进行转换，然后进行求和
        //可以对int值使用summinglong()，但不能对long值使用summingInt()。
        Integer collect3 = list.stream().collect(Collectors.summingInt(String::length));

        List<Integer> integers = Arrays.asList(1,2,3,4,5,6,6);
        Integer collect5 = integers.stream().collect(Collectors.summingInt(x -> x));

        //汇总整数:summarizingInt () 重要
        //它给出集合中值的所有主要算术运算值，如所有值的平均值、最小值、最大值、所有值的计数和总和。
        IntSummaryStatistics collect6 = integers.stream().collect(Collectors.summarizingInt(x -> x));
        collect6.getAverage();

        //summaryStatistics()只有mapTo...方法才能使用
        IntSummaryStatistics intSummaryStatistics = employees.stream().mapToInt(Employee::getAge).summaryStatistics();
        System.out.println(intSummaryStatistics.getAverage());

        //分组函数
        Map<Integer, List<Employee>> collect7 = employees.stream().collect(Collectors.groupingBy(Employee::getAge));
        //指定value的类型
        Map<String, ArrayList<Employee>> collect8 = employees.stream().collect(Collectors.groupingBy(Employee::getName, Collectors.toCollection(ArrayList::new)));

        //多级分组
        Map<Integer, Map<String, List<Employee>>> collect9 = employees.stream().collect(Collectors.groupingBy(Employee::getAge, Collectors.groupingBy(Employee::getName)));
    }

    /**
     * 转化
     */
    @Test
    public void test7(){
        //stream转化为数组
        Employee[] employees = this.employees.stream().filter(e -> e.getAge() > 20).toArray(Employee[]::new);
        System.out.println(employees);

    }

    /**
     * 并行流
     */
    @Test
    public void test8(){


        //1.没有特定顺序
        employees.stream().parallel().forEach(System.out::println);
        //2.有特定顺序
        employees.stream().parallel().forEachOrdered(System.out::println);

    }

    /**
     * 各类练习
     */
    @Test
    public void test99(){
        List<Employee> employees = EmployeeData.getEmployees();

//        employees.stream().collect(Collectors.groupingBy(e -> Arrays.asList(e.getName(),e.getAge())));
//
//        double average = employees.stream().mapToLong(Employee::getAge).summaryStatistics().getAverage();
//
//        Optional<Integer> max = employees.stream().map(Employee::getAge).max(Integer::compareTo);
//
//        long asLong = employees.stream().mapToLong(Employee::getAge).max().getAsLong();


//        List<Employee> collect = employees.stream().sorted().collect(Collectors.toList());
//        for (Employee employee : collect) {
//            System.out.println(employee.toString());
//        }

        List<Person> people = Arrays.asList(new Person("A", 10), new Person("B", 20));
        List<Person> collect1 = people.stream().sorted().collect(Collectors.toList());
        System.out.println(collect1);
    }


    @Data
    class Person{
        private String name;
        private int age;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }





    public static Stream<Character> filterCharacter(String str){
        List<Character> result = new ArrayList<>();
        for (char c : str.toCharArray()) {
            result.add(c);
        }
        return result.stream();
    }







}
